package com.elminster.dao.common.impl;

import java.io.Serializable;
import java.util.List;

import javax.annotation.PostConstruct;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.LockMode;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.elminster.dao.common.IGenericDao;

abstract public class GenericDaoImpl<T, PK extends Serializable> extends HibernateDaoSupport implements IGenericDao<T, PK> {

    private static final Log log = LogFactory.getLog(GenericDaoImpl.class);
    private Class<T> type;
    @Autowired
    protected SessionFactory sessionFactory;
    
    /**
     */
    @PostConstruct
    public void init() {
        setSessionFactory(sessionFactory);
    }
    
    public GenericDaoImpl(Class<T> type) {
        this.type = type;
    }
    
    public void delete(T t) {
        log.debug("deleting instance");
        try {
            getHibernateTemplate().delete(t);
            log.debug("delete successful");
        } catch (RuntimeException re) {
            log.error("delete failed", re);
            throw re;
        }
    }

    @SuppressWarnings("unchecked")
    public T find(PK pk) {
        log.debug("getting instance with pk: " + pk);
        try {
            T t = (T) getHibernateTemplate().get(type, pk);
            return t;
        } catch (RuntimeException re) {
            log.error("get failed", re);
            throw re;
        }
    }
    
    @SuppressWarnings("unchecked")
    public List<T> findByExample(T t) {
        log.debug("finding instance by example");
        try {
            List<T> results = (List<T>) getHibernateTemplate()
                    .findByExample(t);
            log.debug("find by example successful, result size: "
                    + results.size());
            return results;
        } catch (RuntimeException re) {
            log.error("find by example failed", re);
            throw re;
        }
    }

    @SuppressWarnings("unchecked")
    public List<T> findAll() {
        log.debug("finding all instances");
        String queryString = "from " + type.getName();
        try {
            return getHibernateTemplate().find(queryString);
        } catch (RuntimeException re) {
            log.error("find all failed", re);
            throw re;
        }
    }

    @SuppressWarnings("unchecked")
    public int getTotalCount() {
        String hql = "select count (*) from " + type.getName();
        try {
            List list = getHibernateTemplate().find(hql);
            if (list.isEmpty()) {
                return 0;
            } else {
                return new Integer(list.get(0).toString());
            }
        } catch (RuntimeException re) {
            log.error("get total count failed", re);
            throw re;
        }
    }

    public void save(T t) {
        log.debug("saving instance");
        try {
            getHibernateTemplate().save(t);
            log.debug("save successful");
        } catch (RuntimeException re) {
            log.error("save failed", re);
            throw re;
        }
    }

    public void update(T t) {
        log.debug("updating instance");
        try {
            getHibernateTemplate().save(t);
            log.debug("save successful");
        } catch (RuntimeException re) {
            log.error("save failed", re);
            throw re;
        }
    }

    @SuppressWarnings("unchecked")
    public T merge(T t) {
        log.debug("merging instance");
        try {
            T result = (T) getHibernateTemplate().merge(t);
            log.debug("merge successful");
            return result;
        } catch (RuntimeException re) {
            log.error("merge failed", re);
            throw re;
        }
    }

    public void attachDirty(T t) {
        log.debug("attaching dirty instance");
        try {
            getHibernateTemplate().saveOrUpdate(t);
            log.debug("attach successful");
        } catch (RuntimeException re) {
            log.error("attach failed", re);
            throw re;
        }
    }

    public void attachClean(T t) {
        log.debug("attaching clean instance");
        try {
            getHibernateTemplate().lock(t, LockMode.NONE);
            log.debug("attach successful");
        } catch (RuntimeException re) {
            log.error("attach failed", re);
            throw re;
        }
    }
    
    @SuppressWarnings("unchecked")
    protected List<T> findByProperty(String propertyName, Object value) {
        log.debug("finding instance with property: " + propertyName
                + ", value: " + value);
        try {
            String queryString = "from " + type.getName() + " as model where model."
                    + propertyName + "= ?";
            return getHibernateTemplate().find(queryString, value);
        } catch (RuntimeException re) {
            log.error("find by property name failed", re);
            throw re;
        }
    }
    
    @SuppressWarnings("unchecked")
    public List<T> findByHql(String hql, Object[] params) {
        log.debug("finding instance by hql");
        try {
            List<T> results = getHibernateTemplate().find(hql, params);
            log.error("find instance by hql successful");
            return results;
        } catch (RuntimeException re) {
            log.error("find instance by hql failed", re);
            throw re;
        }
    }
}
